package ru.urfc.portal.ui.client.renderers;

import java.util.HashMap;
import java.util.Map;

import ru.urfc.portal.ui.client.descriptor.GridField;

import com.extjs.gxt.ui.client.data.ModelData;
import com.google.gwt.dom.client.Element;

public class MoneyTextRenderer extends SimpleRenderer {
	
	static final String HE = "М";
	static final String SHE = "Ж";
	static Map<String, String> numbers = createNumbers();	
	static Chain copeckChain = new Chain(1, null, SHE, "копейка", "копейки", "копеек");
	static Chain roublesChain = extendChain(new Chain(1, null, HE, "рубль", "рубля", "рублей"));
	static Chain daysChain = new Chain(1, null, HE, "день", "дня", "дней");
	
	public static String renderDay(Integer day) {
		return day + daysChain.renderDegree(day, true);
	}
	
	public MoneyTextRenderer(GridField field, Element element) {
		super(field, element);
	}
	
	static Chain extendChain(Chain chain) {
		return chain.join(SHE, "тысяча", "тысячи", "тысяч")
		.join(HE, "миллион", "миллиона", "миллионов")
		.join(HE, "миллиард", "миллиарда", "миллиардов");
	}

	private static Map<String, String> createNumbers() {
		Map<String, String> numbers  = new HashMap<String, String>();
		String[] words = ("ноль один два три четыре пять шесть семь восемь девять десять" +
				" одиннадцать двенадцать тринадцать четырнадцать пятнадцать" +
				" шестнадцать семнадцать восемнадцать девятнадцать двадцать" +
				" тридцать сорок пятьдесят шестьдесят семьдесят восемьдесят девяносто сто" +
				" двести триста четыреста пятьсот шестсот семьсот восемьсот девятьсот").split(" ");
		Integer n = 0;
		for (int i = 0; i < words.length; ++i) {
			numbers.put(n + HE, words[i]);
			numbers.put(n + SHE, words[i]);
			int step = n < 20? 1: n < 100? 10: 100;
			n += step; 
		}
		numbers.put(1 + SHE, "одна");
		numbers.put(2 + SHE, "две");
		return numbers ;
	}

	@Override
	public String render(ModelData modelData) {
		Double value = field.getValue(modelData);
		Double copeck = value * 100 % 100;
		String result = render(value.intValue(), copeck.intValue());
		return result.substring(0, 1).toUpperCase() + result.substring(1);
	}
	
	String render(Integer amount, Integer copeck) {
		return roublesChain.render(amount, false) + (copeck == 0? "": " " + copeckChain.render(copeck, true));
	}
	
	static class Chain {
		String gender;
		String[] declensions;
		Integer degree;
		Chain next;
		
		public Chain(Integer degree, Chain next, String gender, String... declensions) {
			this.gender = gender;
			this.declensions = declensions;
			this.degree = degree;
			this.next = next;
		}

		Chain join(String gender, String... declensions) {
			return new Chain(degree * 1000, this, gender, declensions);
		}

		String render(Integer amount) {
			return render(amount, false);
		}
		
		String render(Integer amount, boolean hasHead) {
			int head = amount / degree;
			int tail = amount % degree;
			String headStr = head == 0? "": renderLessThan1000(head) + renderDegree(head, true);
			String tailStr = next == null? "": next.render(tail, hasHead || head != 0);
			if (amount == 0 && next == null) {
				return (hasHead? "": renderLessThan1000(head)) + renderDegree(head, !hasHead);
			}
			return headStr + (headStr.length() == 0 || tailStr.length() == 0? "": " ") + tailStr;
		}
		
		private String renderDegree(Integer amount, boolean withSpace) {
			int lastDigits = amount < 20? amount % 20: amount % 10;
			String result = lastDigits == 1? declensions[0]: 1 < lastDigits && lastDigits <= 4?
					declensions[1]: declensions[2];
			return result.length() == 0 || !withSpace? result: " " + result;
		}

		private String renderLessThan1000(Integer amount) {
			if (hasNumber(amount)) {
				return getNumber(amount);
			}
			Integer head = amount > 100?  (amount / 100) * 100: (amount / 10) * 10;
			Integer tail = amount - head;
			return getNumber(head) + " " + renderLessThan1000(tail);
		}

		private String getNumber(Integer amount) {
			return numbers.get(amount + gender);
		}

		private boolean hasNumber(Integer amount) {
			return numbers.containsKey(amount + gender);
		}
		
	}

}
